#ifndef __LINUX_GFP_H
#define __LINUX_GFP_H

#include <linux/mm_types.h>
#include <linux/mmzone.h>
#include <linux/nodemask.h>

#define __GFP_DMA	(( gfp_t)0x01u)
#define __GFP_HIGHMEM	(( gfp_t)0x02u)
#define __GFP_DMA32	(( gfp_t)0x04u)
#define __GFP_MOVABLE	(( gfp_t)0x08u)  /* Page is movable */
#define GFP_ZONEMASK	(__GFP_DMA|__GFP_HIGHMEM|__GFP_DMA32|__GFP_MOVABLE)

#define __GFP_WAIT	(( gfp_t)0x10u)	/* Can wait and reschedule? */
#define __GFP_HIGH	(( gfp_t)0x20u)	/* Should access emergency pools? */
#define __GFP_IO	(( gfp_t)0x40u)	/* Can start physical IO? */
#define __GFP_FS	(( gfp_t)0x80u)	/* Can call down to low-level FS? */
#define __GFP_COLD	(( gfp_t)0x100u)	/* Cache-cold page required */
#define __GFP_NOWARN	(( gfp_t)0x200u)	/* Suppress page allocation failure warning */
#define __GFP_REPEAT	(( gfp_t)0x400u)	/* See above */
#define __GFP_NOFAIL	(( gfp_t)0x800u)	/* See above */
#define __GFP_NORETRY	(( gfp_t)0x1000u)/* See above */
#define __GFP_COMP	(( gfp_t)0x4000u)/* Add compound page metadata */
#define __GFP_ZERO	(( gfp_t)0x8000u)/* Return zeroed page on success */
#define __GFP_NOMEMALLOC (( gfp_t)0x10000u) /* Don't use emergency reserves */
#define __GFP_HARDWALL   (( gfp_t)0x20000u) /* Enforce hardwall cpuset memory allocs */
#define __GFP_THISNODE	(( gfp_t)0x40000u)/* No fallback, no policies */
#define __GFP_RECLAIMABLE (( gfp_t)0x80000u) /* Page is reclaimable */

#define __GFP_NOTRACK	((gfp_t)0)

#define __GFP_BITS_SHIFT 22	/* Room for 22 __GFP_FOO bits */
#define __GFP_BITS_MASK ((gfp_t)((1 << __GFP_BITS_SHIFT) - 1))

#define GFP_ATOMIC	(__GFP_HIGH)
#define GFP_NOFS	(__GFP_WAIT | __GFP_IO)
#define GFP_KERNEL	(__GFP_WAIT | __GFP_IO | __GFP_FS)
#define GFP_USER	(__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL)

/* Control page allocator reclaim behavior */
#define GFP_RECLAIM_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS|\
			__GFP_NOWARN|__GFP_REPEAT|__GFP_NOFAIL|\
			__GFP_NORETRY|__GFP_NOMEMALLOC)

#define GFP_BOOT_MASK __GFP_BITS_MASK & ~(__GFP_WAIT|__GFP_IO|__GFP_FS)

static inline int allocflags_to_migratetype(gfp_t gfp_flags)
{
	// WARN_ON((gfp_flags & GFP_MOVABLE_MASK) == GFP_MOVABLE_MASK);

	// if (unlikely(page_group_by_mobility_disabled))
	// 	return MIGRATE_UNMOVABLE;

	/* Group based on mobility */
	return (((gfp_flags & __GFP_MOVABLE) != 0) << 1) |
		((gfp_flags & __GFP_RECLAIMABLE) != 0);
}

#ifdef CONFIG_HIGHMEM
#define OPT_ZONE_HIGHMEM ZONE_HIGHMEM
#else
#define OPT_ZONE_HIGHMEM ZONE_NORMAL
#endif

#ifdef CONFIG_ZONE_DMA
#define OPT_ZONE_DMA ZONE_DMA
#else
#define OPT_ZONE_DMA ZONE_NORMAL
#endif

#ifdef CONFIG_ZONE_DMA32
#define OPT_ZONE_DMA32 ZONE_DMA32
#else
#define OPT_ZONE_DMA32 ZONE_NORMAL
#endif

#define GFP_ZONE_TABLE ( \
	(ZONE_NORMAL << 0 * ZONES_SHIFT)				\
	| (OPT_ZONE_DMA << __GFP_DMA * ZONES_SHIFT) 			\
	| (OPT_ZONE_HIGHMEM << __GFP_HIGHMEM * ZONES_SHIFT)		\
	| (OPT_ZONE_DMA32 << __GFP_DMA32 * ZONES_SHIFT)			\
	| (ZONE_NORMAL << __GFP_MOVABLE * ZONES_SHIFT)			\
	| (OPT_ZONE_DMA << (__GFP_MOVABLE | __GFP_DMA) * ZONES_SHIFT)	\
	| (ZONE_MOVABLE << (__GFP_MOVABLE | __GFP_HIGHMEM) * ZONES_SHIFT)\
	| (OPT_ZONE_DMA32 << (__GFP_MOVABLE | __GFP_DMA32) * ZONES_SHIFT)\
)

static inline enum zone_type gfp_zone(gfp_t flags)
{
	enum zone_type z;
	int bit = flags & GFP_ZONEMASK;

	z = (GFP_ZONE_TABLE >> (bit * ZONES_SHIFT)) &
					 ((1 << ZONES_SHIFT) - 1);

// 	if (__builtin_constant_p(bit))
// 		MAYBE_BUILD_BUG_ON((GFP_ZONE_BAD >> bit) & 1);
// 	else {
// #ifdef CONFIG_DEBUG_VM
// 		BUG_ON((GFP_ZONE_BAD >> bit) & 1);
// #endif
// 	}
	return z;
}

static inline int gfp_zonelist(gfp_t flags)
{
	// if (NUMA_BUILD && flags & __GFP_THISNODE)
	// 	return 1;

	return 0;
}

static inline struct zonelist *node_zonelist(int nid, gfp_t flags)
{
	return NODE_DATA(nid)->node_zonelists + gfp_zonelist(flags);
}

struct page *
__alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
		       struct zonelist *zonelist, nodemask_t *nodemask);

static inline struct page *
__alloc_pages(gfp_t gfp_mask, unsigned int order,
		struct zonelist *zonelist)
{
	return __alloc_pages_nodemask(gfp_mask, order, zonelist, NULL);
}

#define numa_node_id() 0

static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,
						unsigned int order)
{
	/* Unknown node is current node */
	if (nid < 0)
		nid = numa_node_id();

	return __alloc_pages(gfp_mask, order, node_zonelist(nid, gfp_mask));
}

#define alloc_pages(gfp_mask, order) \
		alloc_pages_node(numa_node_id(), gfp_mask, order)

#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)

extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);

#define __get_free_page(gfp_mask) \
		__get_free_pages((gfp_mask),0)


extern void __free_pages(struct page *page, unsigned int order);
extern void free_pages(unsigned long addr, unsigned int order);

#define __free_page(page) __free_pages((page), 0)
#define free_page(addr) free_pages((addr),0)

#endif
